﻿using AntdUI;
using Azure;
using ImportPlcPointPosition.Controls.Base;
using ImportPlcPointPosition.Extensions;
using ImportPlcPointPosition.Models.Table;
using ImportPlcPointPosition.Models.ViewModels;
using ImportPlcPointPosition.Utils;
using Mapster;
using Microsoft.VisualBasic.ApplicationServices;
using MiniExcelLibs;
using S7.Net;
using SqlSugar;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ZR.DataCollection2.Core;
using ZR.Model.Models.ProductionControl;
using static System.Runtime.InteropServices.JavaScript.JSType;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace ImportPlcPointPosition.Controls.Pages
{
    /// <summary>
    /// 通信点位配置
    /// 左侧显示PLC列表
    ///  选中PLC，加载对应的数据块选项，
    ///  用户可导入或者从数据块加载指定的数据块，对点位数据进行配置
    ///  
    /// </summary>
    public partial class DataPositionPage : BasePointConfigContol
    {
        /// <summary>
        /// MesDBClient
        /// </summary>
        protected readonly SqlSugarClient MesDBClient = SqlSugarHelper.MesDB;

        /// <summary>
        /// Localhost IP
        /// </summary>
        protected readonly string Localhost = "127.0.0.1";

        /// <summary>
        /// 当前PLC实例
        /// </summary>
        private PlcConfig CurrentPlc = null;

        /// <summary>
        /// 存储控件临时列表
        /// </summary>
        //private List<BlockDbTableControl> BlockDbListTemp = new List<BlockDbTableControl>();

        /// <summary>
        /// 本地PLC服务
        /// </summary>
        private S7Server LocalServer = null;


        public DataPositionPage()
        {
            InitializeComponent();
            this.Load += DataPositionPage_Load;
            this.switchListenData.CheckedChanged += SwitchListenData_CheckedChanged;
        }


        #region Methods
        /// <summary>
        /// 刷新页面
        /// </summary>
        public override void ReflushPage()
        {
            LoadPlcDataTable();
        }

        /// <summary>
        /// 初始化左侧表格
        /// </summary>
        private void InitPlcDataTable()
        {
            tablePlcList.Columns = new ColumnCollection() {
                new Column("Memo", "工站编号").SetAlign(),
            };
            tablePlcList.EditMode = TEditMode.None;
            tablePlcList.HandCursor = Cursors.Hand;
            tablePlcList.Bordered = true;
            tablePlcList.EnableHeaderResizing = false;
            tablePlcList.EmptyHeader = true;
            tablePlcList.Gap = 6;
            tablePlcList.VisibleHeader = false;
            tablePlcList.CellClick += TablePlcList_CellClick;
        }

        /// <summary>
        /// 加载左侧表格数据
        /// </summary>
        private void LoadPlcDataTable()
        {
            var lst = MesDBClient.Queryable<PlcConfig>().ToList();
            BindingList<PlcConfig> soureDt = new BindingList<PlcConfig>(lst);
            tablePlcList.Binding(soureDt);
            if (soureDt.Count > 0 && tablePlcList.SelectedIndex < 0)
            {
                tablePlcList.SelectedIndex = 1;
                CurrentPlc = soureDt[tablePlcList.SelectedIndex - 1];
                LoadPlcBlockDbPoint(CurrentPlc);
            }
        }

        /// <summary>
        /// 初始化下拉控件的数据块配置
        /// </summary>
        /// <param name="config"></param>
        private void LoadPlcBlockDbPoint(PlcConfig config)
        {
            pageHeader1.Text = $"通信点位配置 [{config.Id}] - [{config.WorkstationCode}] - [{config.PlcAddress}] - [{config.Remark}]";

            var dbBlockItems = MesDBClient.Queryable<PlcBlockDb>()
                .InnerJoin<PlcConfig>((a, b) => a.PlcId == b.Id)
                .Select((a, b) => new PlcBlockDb() { WorkstationCode = b.WorkstationCode }, true)
                .Where(a => a.PlcId == config.Id)
                .OrderBy(a => a.BlockDb)
                .ToList();

            tabsPlcPointCfgDtl.Pages.Clear();//清空内容

            if (dbBlockItems == null || dbBlockItems.Count == 0)
            {
                switchLocalServer.Enabled = switchListenData.Enabled = false;
                MessageInfo("当前站未配置BD块！");
                return;
            }

            foreach (var blockDb in dbBlockItems)
            {
                string desc = blockDb.WriteFrom switch
                {
                    1 => "PC",
                    2 => "PLC",
                    3 => "混合",
                    _ => ""
                };

                //创建页签项
                var tabPage = new AntdUI.TabPage() { Text = $"{desc}_DB{blockDb.BlockDb}{(blockDb.Remark == "" ? "" : $"【{blockDb.Remark}】")}" };
                tabPage.Cursor = Cursors.Hand;
                tabPage.IconSvg = "BlockOutlined";
                tabPage.Tag = $"page_{blockDb.Id}";

                //创建页签项内容
                var dbPointTable = new BlockDbTableControl(CurrentPlc, blockDb);
                //dbPointTable.BlockDb = blockDb;
                //dbPointTable.CurrentPlc = CurrentPlc;
                dbPointTable.Width = 100;
                dbPointTable.Height = 100;
                dbPointTable.Name = $"page_{blockDb.Id}";
                dbPointTable.Location = new Point(10, 10);
                dbPointTable.Parent = tabPage;
                dbPointTable.Dock = DockStyle.Fill;

                //将【创建页签项内容】添加到该【页签项】
                tabPage.Controls.Add(dbPointTable);


                //将【创建页签项】添加到【页签列表】中
                tabsPlcPointCfgDtl.Pages.Add(tabPage);

                //添加到
                //BlockDbListTemp.Add(dbPointTable);
            }
            switchLocalServer.Enabled = switchListenData.Enabled = true;
        }


        #endregion

        #region Events
        /// <summary>
        /// 页面Load事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataPositionPage_Load(object sender, EventArgs e)
        {
            InitPlcDataTable();
            LoadPlcDataTable();
            _ = StartWatch();
            _ = StartAutoWrite();
        }

        /// <summary>
        /// plc表格单元格点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TablePlcList_CellClick(object sender, TableClickEventArgs e)
        {
            var config = e.Record as PlcConfig;
            CurrentPlc = config;
            LoadPlcBlockDbPoint(CurrentPlc);
        }

        /// <summary>
        /// 数据监听Change
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SwitchListenData_CheckedChanged(object sender, BoolEventArgs e)
        {
            if (e.Value)
            {
                AntdUI.Spin.open(this, "正在开启监视服务，请稍后...", (config) =>
                {
                    try
                    {
                        config.Value = 0.4f;
                        //Thread.Sleep(200);
                        //启动模拟仿真
                        StartLocalServer();
                        //耗时代码，处理数据
                        bool connect = ConnectPlc().IsConnect;
                        switchListenData.Checked = connect;
                        config.Value = 1.0f;
                        if (!connect)
                            MessageError("连接失败");
                        else
                            MessageInfo("连接成功");
                    }
                    catch (Exception ex)
                    {
                        config.Value = 1.0f;
                        MessageError("连接异常:" + ex.Message);
                    }

                }, () =>
                {
                    //结束时执行的代码
                    ChangePointTableColumnVisible();
                });
            }
            else
            {
                DisConnectPlc();
                ChangePointTableColumnVisible();
            }
        }


        #endregion

        /// <summary>
        /// 调用BlockDbTable IsWatch变化
        /// </summary>
        private void ChangePointTableColumnVisible()
        {
            foreach (var page in tabsPlcPointCfgDtl.Pages)
            {
                var item = page.Controls.Find(page.Tag.ToString(), true)?.FirstOrDefault() as BlockDbTableControl;
                item.IsWatch = switchListenData.Checked;
                item.ChangePointTableColumnVisible();
            }
            switchLocalServer.Enabled = !switchListenData.Checked;
        }


        /// <summary>
        /// 监视数据
        /// </summary>
        /// <returns></returns>
        private async Task StartWatch()
        {
            try
            {
                while (this.IsHandleCreated) //
                {
                    try
                    {
                        if (!this.switchListenData.Checked && App.CurrentPlcClient != null)
                            App.CurrentPlcClient.Decoder = null;//重置解析器
                        await WatchPlcData();
                    }
                    catch (Exception)
                    {

                    }

                    await Task.Delay(100); // 每100毫秒读取一次
                }
            }
            catch (Exception ex)
            {

            }
        }

        private async Task StartAutoWrite()
        {
            try
            {
                while (this.IsHandleCreated)
                {
                    await AutoWrite();
                    await Task.Delay(1000); // 每100毫秒读取一次
                }
            }
            catch (Exception ex)
            {

            }
        }


        public async Task AutoWrite()
        {
            try
            {
                if (App.CurrentPlcClient == null || !App.CurrentPlcClient.PlcSession.IsConnected)
                    return;

                List<Task> tasks = new List<Task>();
                foreach (var page in tabsPlcPointCfgDtl.Pages)
                {
                    var dbPanel = page.Controls[0] as BlockDbTableControl;
                    var task = Task.Run(() =>
                    {
                        CheckWriteTable(dbPanel.CurrentPointDatas.ToList());
                    });
                    tasks.Add(task);
                }
                await Task.WhenAll(tasks);
            }
            catch (Exception)
            {

            }
        }

        private static void CheckWriteTable(List<ImportData> datas)
        {

            foreach (var item in datas)
            {
                WriteAutoVal(item);
            }
        }

        private static void WriteAutoVal(ImportData item)
        {
            if (item.AutoWrite)
            {
                var items = item.WriteValue.Split(',');
                item.LastWriteIndex++;
                if (item.LastWriteIndex >= items.Length)
                {
                    item.LastWriteIndex = 0;
                }
                var writeModel = item.Adapt<CksPlcDataPosition>();
                string writeObject = items[item.LastWriteIndex];
                //如果是字符串 ，自动生成一个编码 CKS
                if (writeModel.ParseType.Equals("string"))
                {
                    var dt = DateTime.Now;
                    writeObject = $"CKS{dt.ToString("yyyyMMddHHmmss")}";
                }
                App.CurrentPlcClient.SetValue(writeModel, writeObject);
            }

            if (item.SubItems != null && item.SubItems.Length > 0)
            {
                CheckWriteTable(item.SubItems.ToList());
            }
        }

        int LastWatchDB = 0;

        /// <summary>
        /// 监听PLC数据、遍历数据块
        /// </summary>
        /// <returns></returns>
        private async Task WatchPlcData()
        {
            await Task.Yield();

            if (App.CurrentPlcClient == null || !App.CurrentPlcClient.PlcSession.IsConnected)
                return;

            if (CurrentPlc == null)
                return;

            if (tabsPlcPointCfgDtl.Pages.Count <= 0)
                return;

            //只监视当前显示的db块
            int exceptiondb = 0;
            try
            {

                List<Task> tasks = new List<Task>();
                foreach (var page in tabsPlcPointCfgDtl.SelectedTab.Controls)
                {
                    var task = Task.Run(async () =>
                    {
                        var dbPanel = page as BlockDbTableControl;
                        var PointDatas = ExpandPointDataToList(dbPanel.CurrentPointDatas);
                        var plcdb = dbPanel.CurrentPlcBlockDb;
                        var plcSession = App.CurrentPlcClient?.PlcSession;
                        exceptiondb = plcdb.BlockDb;
                        var blockBytes = await plcSession.ReadBytesAsync(DataType.DataBlock, plcdb.BlockDb, 0, plcdb.BlockLength);
                        var points = PointDatas.Adapt<List<CksPlcDataPosition>>();
                        SieminDecoder decoder = App.CurrentPlcClient?.Decoder;
                        bool isFirst = false;
                        if (decoder == null || LastWatchDB != plcdb.BlockDb)
                        {
                            decoder = App.CurrentPlcClient.Decoder = new SieminDecoder(points);
                            isFirst = true;
                        }


                        LastWatchDB = plcdb.BlockDb;
                        var changeValues = decoder.Decode(blockBytes).ToList();
                        if (!isFirst)
                        {
                            changeValues = changeValues.FindAll(a => a.IsChange);
                        }


                        foreach (var item in changeValues)
                        {
                            var one = PointDatas.Where(a => a.Id == item.Position.Id).First();
                            if (one != null)
                            {

                                //判断是否是数组 ， 如果是数组，更新数组的监视值
                                if (SieminDecoder.IsArrayType(one.ParseType, out int len, out string tt))
                                {

                                    if (one.SubItems != null && tt.IsS7Int16())
                                    {
                                        var ArrayVals = item.Value.Adapt<List<int>>();
                                        for (int i = 0; i < one.SubItems.Count(); i++)
                                        {
                                            one.SubItems[i].WatchValue = ArrayVals[i].ToString();
                                        }
                                    }
                                    else if (one.SubItems != null && tt.ToLower().Equals("real"))
                                    {
                                        var ArrayVals = item.Value.Adapt<List<decimal>>();
                                        for (int i = 0; i < one.SubItems.Count(); i++)
                                        {
                                            one.SubItems[i].WatchValue = ArrayVals[i].ObjToDecimal().ToString();
                                        }
                                    }
                                    else if (one.SubItems != null && (item.Value is Array || item.Value is System.Collections.IList))
                                    {
                                        var ArrayVals = item.Value.Adapt<List<string>>();
                                        for (int i = 0; i < one.SubItems.Count(); i++)
                                        {
                                            one.SubItems[i].WatchValue = ArrayVals[i];
                                        }
                                        if (item.DecodeObject.IsCharArray)
                                        {
                                            one.WatchValue = item.DecodeObject.CharString;
                                        }
                                    }

                                }
                                else
                                {
                                    one.WatchValue = item.Value.ToString();
                                }
                            }
                        }
                    });
                    tasks.Add(task);

                }

                await Task.WhenAll(tasks);



            }
            catch (Exception ex)
            {
                if (ex is S7.Net.PlcException s7ex)
                {
                    if (s7ex.ErrorCode == ErrorCode.ConnectionError)
                        return;
                }
                else if (ex.InnerException is System.Net.Sockets.SocketException socketEx)
                {
                    //由于线程退出或应用程序请求，已放弃I/O 操作。
                    //这个错误一般是由于peer socket被closesocket或者WSAcleanup关闭后，针对这些socket的pending overlapped l/0 operation被中止
                    if (socketEx.ErrorCode == 995)
                        return;
                }
                MessageBoxError($"读取【{exceptiondb}】出错了，{ex.Message}，请尽快处理！\r\n{ex.StackTrace}");
                switchListenData.Checked = false;
                //Received error from PLC: Address out of range :这个错是DB块和配置的DB块不匹配，找PLC重新要一份DB文件
            }

        }


        #region PLC连接部分
        /// <summary>
        /// 启动本地仿真PLC
        /// </summary>
        private void StartLocalServer()
        {
            LocalServer = new S7Server();
            LocalServer.StartTo(Localhost);
            //模拟数据需要制造多个DB块
            foreach (var page in tabsPlcPointCfgDtl.Pages)
            {
                var p = page.Controls.Find(page.Tag.ToString(), true)?.FirstOrDefault() as BlockDbTableControl;
                var Buffer = new byte[p.CurrentPlcBlockDb.BlockLength];
                LocalServer.RegisterArea(S7Server.SrvAreaDB, p.CurrentPlcBlockDb.BlockDb, ref Buffer, Buffer.Length);
            }
        }

        /// <summary>
        /// 关闭本地仿真PLC
        /// </summary>
        private void CloseLocalServer()
        {
            try
            {
                if (LocalServer != null)
                {
                    LocalServer.Stop();
                    LocalServer = null;
                }
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// 连接PLC
        /// </summary>
        /// <returns></returns>
        private (bool IsConnect, string Message) ConnectPlc()
        {
            if (CurrentPlc == null)
            {
                switchListenData.Checked = false;
                return (false, "");
            }

            //if (BlockDbListTemp.Count == 0)
            //{
            //    switchListenData.Checked = false;
            //    return (false, "");
            //}

            //使用全局plcClient对象；
            bool IsConnect = false;
            lock (App.LockClient)
            {
                //先关闭连接，确保每次只有一个plc连接实例
                App.CurrentPlcClient?.Close();
                App.CurrentPlcClient = null;

                try
                {
                    string connectIp = switchLocalServer.Checked ? Localhost : CurrentPlc.PlcAddress;
                    App.CurrentPlcClient = new PLC.PlcClient(connectIp);
                    IsConnect = App.CurrentPlcClient.Connect();
                }
                catch (Exception)
                {
                    App.CurrentPlcClient = null;
                    throw;
                }
                if (!IsConnect)
                    App.CurrentPlcClient = null;
            }

            this.Invoke(() =>
            {
                tablePlcList.Enabled = !IsConnect;//禁用左侧plc选择
            });

            return (IsConnect, "");
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnectPlc()
        {
            CloseLocalServer();
            lock (App.LockClient)
            {
                if (App.CurrentPlcClient != null)
                {
                    App.CurrentPlcClient.Close();
                    App.CurrentPlcClient = null;
                    MessageInfo("连接已关闭");
                }
                try
                {
                    if (this.IsHandleCreated)
                        this.Invoke(() =>
                        {
                            tablePlcList.Enabled = true;
                        });
                }
                catch (Exception)
                {
                }

            }
        }
        #endregion

    }
}
